/*
 * Buffer.h
 *
 *  Created on: 2012-9-3
 *      Author: yangentao@gmail.com
 */

#ifndef BUFFER_H_
#define BUFFER_H_

#include "../dep.h"

#include "ActionManager.h"
#include "Pos.h"
#include "TextListener.h"
#include "LineManager.h"

using namespace std;

//IMPORTANT!!
//XXX if need removeXXXListener, vector<XXXListener*> should clone when fire!

class TextBuffer;

class AutoActionGroup {
private:
	TextBuffer* textBuffer;
public:
	AutoActionGroup(TextBuffer* textBuffer_);
	virtual ~AutoActionGroup();
};

class TextVector: public SplitVec<char> {
public:
	TextVector();
};

class TextSegment {
public:
	const char* buffer;
	int length;
public:
	TextSegment();
	TextSegment(const char* buf, int len);
	TextSegment(const char* buf, int from, int len);
};

class TextBuffer: public ITextReplacable {
	friend class AutoActionGroup;
protected:
	TextVector text;
	Vector<TextListener*> textListeners;
	ActionManager actionMgr;
	LineManager lineMgr;
	PosManager posMgr;
public:
	TextBuffer();
	virtual ~TextBuffer();
	void dump();
public:
	void addTextListener(TextListener* textListener);

	LineManager* getLineManager();
	/// Retrieving positions outside the range of the buffer works and returns 0
	char getChar(int position) const;
	String getText(int position, int length) const;
	const char* bufferPointer();
	int length() const;
	String getText() const; //for debug use
	void setText(String text);

	int getLineCount();
	int getLineStart(size_t line);
	int findLine(int pos);
	String getLineText(int line);

	Pos createPos(int index);

	void beginGroup();
	void endGroup();
	void doAction(const Action& action);
	void undo();
	void redo();
	bool canUndo();
	bool canRedo();
	void clearUndoHistory();
protected:
	//return old text
	String textReplace(int from, int len_, const String& newText);
};

inline String TextBuffer::getLineText(int line) {
	String s;
	int from = getLineStart(line);
	if (line + 1 < getLineCount()) {
		int to = getLineStart(line + 1);
		s = getText(from, to - from);
	} else {
		s = getText(from, length() - from);
	}
	if (!s.empty() && s.last() == 10) {
		s.setLength(s.length() - 1);
	}
	if (!s.empty() && s.last() == 13) {
		s.setLength(s.length() - 1);
	}
	return s;
}

inline void TextBuffer::dump() {
	text.dump();
	lineMgr.dump();
}
inline LineManager* TextBuffer::getLineManager() {
	return &lineMgr;
}
/// Retrieving positions outside the range of the buffer works and returns 0
inline char TextBuffer::getChar(int position) const {
	return text.get(position);
}
inline const char* TextBuffer::bufferPointer() {
	return text.getBuffer();
}
inline int TextBuffer::length() const {
	return text.length();
}

inline int TextBuffer::getLineCount() {
	return lineMgr.count();
}
inline int TextBuffer::getLineStart(size_t line) {
	return lineMgr.getLineStart(line);
}
inline int TextBuffer::findLine(int pos) {
	return lineMgr.findLine(pos);
}

inline Pos TextBuffer::createPos(int index) {
	if (index >= 0 && index <= text.length()) {
		return posMgr.createPos(index);
	} else {
		throw InvalidIndexException();
	}
}

inline void TextBuffer::beginGroup() {
	actionMgr.beginGroup();
}
inline void TextBuffer::endGroup() {
	actionMgr.endGroup();
}
inline void TextBuffer::doAction(const Action& action) {
	actionMgr.doAction(action);
}
inline void TextBuffer::undo() {
	actionMgr.undo();
}
inline void TextBuffer::redo() {
	actionMgr.redo();
}
inline bool TextBuffer::canUndo() {
	return actionMgr.canUndo();
}
inline bool TextBuffer::canRedo() {
	return actionMgr.canRedo();
}
inline void TextBuffer::clearUndoHistory() {
	actionMgr.clear();
}

inline TextSegment::TextSegment() {
	buffer = 0;
	length = 0;
}
inline TextSegment::TextSegment(const char* buf, int len) {
	buffer = buf;
	length = len;
}
inline TextSegment::TextSegment(const char* buf, int from, int len) {
	buffer = buf + from;
	length = len;
}
#endif /* BUFFER_H_ */
